1 module dataframe.csv; 2 import dataframe.common; 3 import dataframe.variant; 4 import dataframe.typed; 5 import std.conv; 6 import std.csv; 7 import std.datetime; 8 import std.exception; 9 import std.range:array, stride,only; 10 import std.stdio; 11 import std.variant; 12 import std.string:isNumeric; 13 import std.typecons:tuple,Tuple; 14 import std.traits; 15 import std.file; 16 17 private size_t peekCols(string data, char separator=',') 18 { 19 import std.string:indexOf,split; 20 auto i=indexOf(data,"\n"); 21 return (i==-1)?0:(data[0..i].split([separator]).length); 22 } 23 private string[] peekHeaderCols(string data, char separator=',') 24 { 25 import std.string:indexOf,split; 26 auto i=indexOf(data,"\n"); 27 return (i==-1)?[]:(data[0..i].split([separator])); 28 } 29 30 DataFrame loadCSV(Malformed errorLevel=Malformed.ignore)(string data, bool hasHeader=false, char separator=',') 31 { 32 // auto csv=csvReader!(string, errorLevel)(data, separator, quote); 33 DataFrame ret; 34 auto csv=csvReader(data,separator); // , separator, quote); 35 size_t i; 36 ret.setCellColumns(data.peekCols-1); // since first col is the index value 37 if (hasHeader) 38 { 39 auto cols=peekHeaderCols(data,separator); 40 ret.setColumnTitles(cols[1..$]); 41 ret.setIndexTitle(cols[0]); 42 } 43 bool firstRow=true; 44 foreach(row;csv) 45 { 46 if (firstRow && hasHeader) 47 { 48 firstRow=false; 49 continue; 50 } 51 size_t j; 52 foreach(col;row) 53 { 54 if (j==0) 55 ret.indexValues~=col.to!KalVariant; 56 else 57 { 58 ret.cellValues.length+=ret.numCols; 59 ret[i,j]=col; 60 } 61 ++j; 62 } 63 ++i; 64 } 65 return ret; 66 } 67 68 DataFrame saveCSV(DataFrame frame, string filename, bool useHeader=true) 69 { 70 string ret; 71 if(useHeader) 72 { 73 foreach(j;0..frame.numCols) 74 ret~=frame.columnTitles[j]~","; 75 ret=ret[0..$-1]~"\n"; 76 } 77 foreach(i;0..frame.numRows) 78 { 79 string rowString=""; 80 foreach(j;0..frame.numCols) 81 rowString~=frame[i,j].to!string~","; 82 rowString=rowString[0..$-1]~"\n"; 83 ret~=rowString; 84 } 85 std.file.write(filename,ret); 86 return frame; 87 } 88 89 90 91 DataFrameTyped loadCSV(Malformed errorLevel=Malformed.ignore)(string data, string[] columnTitles=[], bool skipFirst=false,char separator=',') 92 { 93 import std.string:strip; 94 import std.range:enumerate; 95 DataFrameTyped ret; 96 97 // auto csv=csvReader!(string, errorLevel)(data, separator, quote); 98 auto csv=csvReader(data,separator); // , separator, quote); 99 size_t i; 100 auto peek=data.peekCols; 101 bool hasHeader=(columnTitles.length==0); 102 if (hasHeader) 103 ret.setColumnTitles(peekHeaderCols(data,separator)); 104 else 105 { 106 ret.setColumnTitles(columnTitles); 107 enforce(peek==columnTitles.length); 108 } 109 bool firstRow=true; 110 111 foreach(row;csv) 112 { 113 if (firstRow && (hasHeader || skipFirst)) 114 { 115 firstRow=false; 116 continue; 117 } 118 size_t j; 119 ret.length=ret.length+1; 120 bool f=false; 121 foreach(k,col;enumerate(row)) 122 { 123 if(k==0) 124 continue; 125 if(col.strip.length>0) 126 { 127 if(col.isNumeric) 128 { 129 if(col.to!double!=0.0) 130 { 131 f=true; 132 break; 133 } 134 } 135 else 136 { 137 f=true; 138 break; 139 } 140 } 141 } 142 if (!f) 143 continue; 144 foreach(col;row) 145 { 146 string colName=columnTitles[j]; 147 final switch(ret.columnTypes[colName]) with(ColumnType) 148 { 149 case String: 150 ret[i,colName]=col; 151 break; 152 case Int: 153 ret[i,colName]=col.to!int; 154 break; 155 case Long: 156 ret[i,colName]=col.to!long; 157 break; 158 case ColumnType.Date: 159 ret.values.dates[colName][i]=parseDate!(std.datetime.Date)(col); 160 break; 161 case ColumnType.DateTime: 162 ret.values.dateTimes[colName][i]=parseDate!(std.datetime.DateTime)(col); 163 break; 164 case Double: 165 ret[i,colName]=(col.strip.length==0)?double.nan:col.to!double; 166 break; 167 } 168 ++j; 169 } 170 ++i; 171 } 172 return ret; 173 } 174 175 DataFrameTyped saveCSV(DataFrameTyped frame, string filename, bool useHeader=true) 176 { 177 string ret; 178 foreach(j;0..frame.numCols) 179 ret~=frame.columnTitles[j]~","; 180 ret=ret[0..$-1]~"\n"; 181 182 foreach(i;0..frame.numRows) 183 { 184 string rowString=""; 185 foreach(j;0..frame.numCols) 186 rowString~=frame.loadCell!string(i,frame.columnTitles[j])~","; 187 rowString=rowString[0..$-1]~"\n"; 188 ret~=rowString; 189 } 190 std.file.write(filename,ret); 191 return frame; 192 } 193